package es.ucm.fdi.bdt;

import java.util.Arrays;
import java.util.Scanner;

import rice.Continuation;
import rice.environment.Environment;
import rice.p2p.commonapi.Application;
import rice.p2p.commonapi.Endpoint;
import rice.p2p.commonapi.Id;
import rice.p2p.commonapi.Message;
import rice.p2p.commonapi.NodeHandle;
import rice.p2p.commonapi.RouteMessage;
import rice.p2p.past.Past;
import rice.p2p.past.PastImpl;
import rice.pastry.PastryNode;
import rice.pastry.commonapi.PastryIdFactory;
import rice.persistence.LRUCache;
import rice.persistence.MemoryStorage;
import rice.persistence.PersistentStorage;
import rice.persistence.Storage;
import rice.persistence.StorageManagerImpl;
import rice.tutorial.past.MyPastContent;

public class App implements Application {

	protected Endpoint endpoint;
	private PastryNode node;
	//private List<NodeHandle> nodes;
	private Environment env;
	private Past app;
	private Storage stor;
	private PastryIdFactory idf;
	private boolean exit;

	public App(PastryNode node, Environment env) {
		this.node = node;
		this.env = env;

		// We are only going to use one instance of this application on each
		// PastryNode
		this.endpoint = node.buildEndpoint(this, "myinstance");

		// the rest of the initialization code could go here
		//nodes = new ArrayList<NodeHandle>();

		// used for generating PastContent object Ids.
		// this implements the "hash function" for our DHT
		idf = new rice.pastry.commonapi.PastryIdFactory(env);

		// create a different storage root for each node
		String storageDirectory = "./storage" + node.getId().hashCode();		

		// create the persistent part
		try {
			stor = new PersistentStorage(idf, storageDirectory,
					4 * 1024 * 1024, node.getEnvironment());
			app = new PastImpl(node, new StorageManagerImpl(idf, stor,
					new LRUCache(new MemoryStorage(idf), 512 * 1024,
							node.getEnvironment())), 3, "");
		} catch (Exception e) {
			e.printStackTrace();
		}

		// now we can receive messages
		this.endpoint.register();
	}

	@SuppressWarnings("unchecked")
	public void run() {
		System.out.println("Node ID: " + node.getId());
		Scanner sc = new Scanner(System.in);
		exit = false;
		while (!exit) {
			System.out.print("> ");
			String line = sc.nextLine();
			if (line.equalsIgnoreCase("exit")) {
				cerrarAplicacion();
			} else if (line.startsWith("send")) {
				publicarMensaje(line.replace("send ", ""));	
			} else if (line.startsWith("read")) {
				leerMensaje();
			}
		}
		sc.close();
		System.exit(0);
	}
	
	public void cerrarAplicacion(){
		exit = true;		
	}
	
	public void publicarMensaje(String msg){
		Id msgId = idf.buildId("mensajeria");
		
		MyPastContent mpc = new MyPastContent(msgId, msg);
		
		app.insert(mpc, new Continuation<Boolean[], Exception>(){
			@Override
			public void receiveResult(Boolean[] result) {
				System.out.println("Successfully inserted up " + Arrays.toString(result) + ".");
			}			
			@Override
			public void receiveException(Exception result) {
				System.out.println("Error looking up " + result);
				result.printStackTrace();
				
			}		
		});
	}
	
	public void leerMensaje(){
		Id msgId = idf.buildId("mensajeria");

		app.lookup(msgId, new Continuation() {

			public void receiveResult(Object result) {
				System.out.println("Successfully looked up " + result + " for key " + result + ".");
			}

			public void receiveException(Exception result) {
				System.out.println("Error looking up " + result);
				result.printStackTrace();
			}

		});
	}
	
	
	@Override
	public void deliver(Id id, Message msg) {
		System.out.println("\ndeliver: " + id.toString() + " -> "
				+ ((Msg) msg).toString() + "\n");

	}

	@Override
	public boolean forward(RouteMessage rmsg) {
		//System.out.println("\nforward: " + rmsg.getMessage() + "\n");

		return false;
	}

	@Override
	public void update(NodeHandle nodehandle, boolean arg1) {
		//System.out.println("\nupdate: " + nodehandle.toString() + " - " + arg1
		//		+ "\n");
		//nodes.add(nodehandle);

	}

	public PastryNode getNode() {
		return node;
	}

}
